/*******************************************************************
This file is part of iBattery.

iBattery is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

iBattery is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with iBattery.  If not, see <http://www.gnu.org/licenses/>.

********************************************************************

iBattery is based on LVMTopBat by levenum, available at 
http://forum.xda-developers.com/showthread.php?t=277790

iBattery info and bug reports:
allleimo@interfree.it
*******************************************************************/

#include <windows.h>
#include <Msgqueue.h>
#include <pm.h>
#include "resource.h"
#include <aygshell.h>	//needed by SHLoadImageFile

#define PUBLIC_VERSION

HINSTANCE g_hInst;
HANDLE	  g_msgQueue;
//bool	  bSingleColor;

DWORD WINAPI NotificationThreadProc(LPVOID lpParameter)
{
	BYTE	buffer[255];
	DWORD	bytesRead, flags;

	
	while(1) //this thread always runs
	{
		WaitForSingleObject(g_msgQueue, INFINITE);
		ReadMsgQueue(g_msgQueue, buffer, 255, &bytesRead, 0,  &flags); //get rid of the message
		InvalidateRect((HWND)lpParameter, NULL, FALSE); //repaint window
	}

	return 0;
}

//meter window proc
LRESULT CALLBACK BatteryMeterWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	static HBITMAP	meterBmp;
	//static SHELLEXECUTEINFO si;

	SYSTEM_POWER_STATUS_EX	ps;
	PAINTSTRUCT				pntStrc;
	BYTE					unitsFull, fullColor;
	HDC						hDC, memDC, origDC;
	HBITMAP					memBmp;
	int						i;
//	int						k;  //bitmap height

#ifdef PUBLIC_VERSION //this is for the release version
	RECT	winRect, parentRect;
#endif

	switch (uMsg)
	{
	case WM_CREATE:

		//commented out, if I click the bar by error I don't want to launch the cpl

		//memset(&si, 0, sizeof(si));
		//si.cbSize = sizeof(si);
		//si.fMask = SEE_MASK_NOCLOSEPROCESS;
		//si.lpDirectory = L"\\windows";
		//si.lpFile = L"ctlpnl.exe";
		//si.lpParameters = L"cplmain.cpl,3,0";
		//si.lpVerb = L"open";
		//si.nShow = SW_SHOW;

		// load the bitmap from iBatteryBmp.bmp file
		// if the file is missing, use the resource bmp
		// bSingleColor = false;

		if (meterBmp = SHLoadImageFile(TEXT("\\windows\\iBatteryBmp.bmp")))
		{
		}
		else
		{
			meterBmp = LoadBitmap(g_hInst, MAKEINTRESOURCE(IDB_METER));
		}

		// check the size of the bitmap, if the height = 6 then we'll use
		// a single color for all battery states, setting bSingleColor to true
		//BITMAP bitmap;
		//GetObject(meterBmp,sizeof(BITMAP),&bitmap);
		//k = bitmap.bmHeight;

		//if(k == 6)
		//	bSingleColor = true;

		return 0;
		break;

	case WM_DESTROY: //cleanup:	
		DeleteObject(meterBmp);
		return 0;
		break;

    // commented out, if I click the bar by error I don't want to launch the cpl 
	case WM_LBUTTONUP:
		//ShellExecuteEx(&si);
	case WM_LBUTTONDOWN:
	case WM_MOUSEMOVE:
		return 0;
		break;

	case WM_PAINT: //this does all the work:
		GetSystemPowerStatusEx(&ps, TRUE); //set initial values

#ifdef PUBLIC_VERSION
		GetClientRect(GetParent(hWnd), &parentRect);
		GetClientRect(hWnd, &winRect);

		//if orientation changed, resize window:
		if ((parentRect.right) != winRect.right)
		{
			winRect.right = parentRect.right;
			SetWindowPos(hWnd, NULL, 0, 0, winRect.right, 3, SWP_NOZORDER | SWP_NOMOVE);
		}
#endif

		hDC = BeginPaint(hWnd, &pntStrc);

		//create back buffer:
		memDC = CreateCompatibleDC(hDC);
		origDC = CreateCompatibleDC(hDC);

#ifndef PUBLIC_VERSION //if this is a public version, adjust width to windows size:
		memBmp = CreateCompatibleBitmap(hDC, 240, 3);  
#else
		memBmp = CreateCompatibleBitmap(hDC, winRect.right, 3); 
#endif

		SelectObject(memDC, memBmp);
		SelectObject(origDC, meterBmp);

		// draw it according to the bitmap size: if height == 3, we use a single color
		// for all the battery states
		// if height == 18, draw according to battery status
		//if(bSingleColor)
		//{
		//	fullColor = 3;
		//}
		//else
		//{
			switch (ps.BatteryFlag) //draw according to battery status  
			{
			case 1: //high charge
				fullColor = 9; //green bars
			case 2: //low charge
				if (ps.BatteryFlag == 2) fullColor = 6;
			case 4: //critical charge
				if (ps.BatteryFlag == 4) fullColor = 3;
			case 8: //charging battery
				if (ps.BatteryFlag == 8) fullColor = 12;
				unitsFull = ps.BatteryLifePercent / 5;
				if (ps.BatteryLifePercent % 5) unitsFull++;
				break;

			case 128: //no battery
				unitsFull = 0;
				break;

			case 255: //unknown status
				unitsFull = 20;
				fullColor = 15;
				break;
			}
		//}

		//just in case:
		if (unitsFull > 20)	unitsFull = 20;

		//paint full bars:
		for (i = 0; i < unitsFull; i++)

#ifndef PUBLIC_VERSION
			BitBlt(memDC, i * 12, 0, 12, 3, origDC, 0, fullColor, SRCCOPY); 
#else
		StretchBlt(memDC, i * (winRect.right / 20), 0, (winRect.right / 20), 3, origDC, 0,
																	fullColor, 12, 3, SRCCOPY);
#endif

		//paint full bars:
		for (i = unitsFull; i < 20; i++)

#ifndef PUBLIC_VERSION
			BitBlt(memDC, i * 12, 0, 12, 3, origDC, 0, 0, SRCCOPY);	
#else
		StretchBlt(memDC, i * (winRect.right / 20), 0, (winRect.right / 20), 3, origDC, 0, 0, 12,
																					3, SRCCOPY); 
#endif
		
		//copy double buffer:
#ifndef PUBLIC_VERSION
		BitBlt(hDC, 0, 0, 240, 3, memDC, 0, 0, SRCCOPY); 
#else
		BitBlt(hDC, 0, 0, winRect.right, 3, memDC, 0, 0, SRCCOPY); 
#endif
		
		//cleanup:
		DeleteDC(origDC);
		DeleteDC(memDC);
		DeleteObject(memBmp);
		EndPaint(hWnd, &pntStrc);
		
		return 0;
		break;	
	}

	return DefWindowProc(hWnd, uMsg, wParam, lParam);
}

int WINAPI WinMain(	HINSTANCE hInstance,
					HINSTANCE hPrevInstance,
					LPTSTR    lpCmdLine,
					int       nCmdShow)
{
	MSG				msg;
	HWND			parent;
	WNDCLASS		wc;
	MSGQUEUEOPTIONS	qOptions;
	HWND			hWnd;
	HANDLE			pnh;
	
	//find taskbar window:
	parent = FindWindow(TEXT("HHTaskBar"), NULL);

	if (!parent) return 0;

	g_hInst = hInstance;

	//register class:
	memset(&wc, 0, sizeof(wc));
	wc.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
	wc.hInstance = hInstance;
	wc.lpfnWndProc = BatteryMeterWndProc;
	wc.lpszClassName = TEXT("iBattery_Class");
	wc.style = CS_HREDRAW | CS_VREDRAW | CS_PARENTDC;

	RegisterClass(&wc);

	//setup message queue for power notifications:
	qOptions.dwSize = sizeof(qOptions);
	qOptions.dwFlags = MSGQUEUE_ALLOW_BROKEN;
	qOptions.dwMaxMessages = 0;
	qOptions.cbMaxMessage = 255;
	qOptions.bReadAccess = TRUE;

	g_msgQueue = CreateMsgQueue(TEXT("iBatteryPwrNotify"), &qOptions);
	if (!g_msgQueue) return 0;

	//signup for power notifications:
	pnh = RequestPowerNotifications(g_msgQueue, PBT_POWERSTATUSCHANGE | PBT_POWERINFOCHANGE);
	if(!pnh) return 0;


	//create window:
	hWnd = CreateWindow(TEXT("iBattery_Class"), NULL, WS_CHILD | WS_VISIBLE, 0, 0, 240, 3, parent,	
						NULL, hInstance, NULL);
	if (!hWnd) return 0;

	CreateThread(NULL, 0, NotificationThreadProc, hWnd, 0, NULL);
	
	while (GetMessage(&msg, NULL, 0, 0)) 
	{	
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}

	StopPowerNotifications(pnh);
	CloseMsgQueue(g_msgQueue);
 	
	return 0;
}

